package com.diors.commons.network.module;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.diors.commons.log.DiorsLogger;
import com.diors.commons.network.module.exception.ModuleRegisteredException;
import com.diors.commons.network.module.model.CommandInfoModel;
import com.diors.commons.network.module.model.CommandModel;
import com.diors.commons.network.module.model.MethodModel;

/**
 * 网络模块管理类
 * @author	darren.ouyang
 * @data	2013-9-10
 */
public class ModuleManager 
{
	
	private static DiorsLogger log = DiorsLogger.getLogger(ModuleManager.class);

	private final static Map<Integer, MethodModel> receivables = new HashMap<Integer, MethodModel>();
	
	private final static Map<Integer, CommandModel> sendables = new HashMap<Integer, CommandModel>();
	
	/**
	 * 注册控制器
	 * @param key			key
	 * @param controller	控制器实体
	 * @throws Exception
	 * @create	2013-9-11	darren.ouyang
	 */
	public static void register (int key, IController controller) throws Exception
	{
		Class<? extends IController> clz = controller.getClass();
		Method[] methods = clz.getDeclaredMethods();
		List<CommandModel> command = controller.getCommands();
		
		Map<Integer, Method> methodMap = getMethodMap(clz, methods);
		Map<Integer, MethodModel> methodModelMap = getMethodModel(controller, command, methodMap);
		Map<Integer, CommandModel> sendableMap = getSendableMap(command);
		
		registerReceivables(key, methodModelMap);
		registerSendables(key, sendableMap);
		StringBuilder builder = new StringBuilder("loading: 类[").append(clz.getName()).append("]加载收到的消息[");
		builder.append(methodModelMap.size()).append("]条，发送的消息[").append(sendableMap.size()).append("]条");
		log.info(builder.toString());
	}

	/**
	 * 获得接到到的包信息
	 * @param key
	 * @param opcode
	 * @return
	 */
	public static MethodModel getReceivable(int key, int opcode)
	{
		MethodModel model = receivables.get(getId(key, opcode));
		
		if(model == null)
		{
			StringBuilder builder = new StringBuilder("没有找到模块[").append(key).
					append("]命令[").append(opcode).append("]接收包信息.");
			NullPointerException e = new NullPointerException(builder.toString());
			log.error(builder.toString(), e);
			throw e;
		}
		return model;
	}

	/**
	 * 获得发送出去的包信息
	 * @param key
	 * @param opcode
	 * @return
	 */
	public static CommandModel getSendable(int key, int opcode)
	{
		CommandModel model = sendables.get(getId(key, opcode));
		
		if(model == null)
		{
			StringBuilder builder = new StringBuilder("没有找到模块[").append(key).
					append("]命令[").append(opcode).append("]发送包信息.");
			NullPointerException e = new NullPointerException(builder.toString());
			log.error(builder.toString(), e);
			throw e;
		}
		return model;
	}

	/**
	 * 关闭所有模块
	 */
	public static void shutdown()
	{
		receivables.clear();
		sendables.clear();
	}

	/**
	 * 注册多个接收包
	 * @param key
	 * @param map
	 */
	private static void registerReceivables(int key, Map<Integer, MethodModel> map)
	{
		Set<Entry<Integer, MethodModel>> sets = map.entrySet();
		
		for(Entry<Integer, MethodModel> e : sets)
		{
			receivables.put(getId(key, e.getKey()), e.getValue());
		}
	}
	
	/**
	 * 根据模块ID和命令号获得解析数据的ID
	 * @param key1
	 * @param key2
	 * @return
	 */
	final static int getId(int key1, int key2)
	{
		int id = key1 << 16;
		
		id += key2;
		
		return id;
	}
	
	/**
	 * 注册多个发送包
	 * @param key
	 * @param map
	 */
	private static void registerSendables(int key, Map<Integer, CommandModel> map)
	{
		Set<Entry<Integer, CommandModel>> sets = map.entrySet();
		
		for(Entry<Integer, CommandModel> e : sets)
		{
			sendables.put(getId(key, e.getKey()), e.getValue());
		}
	}
	
	/**
	 * 获取发送出去的命令
	 * @param commands
	 * @return
	 */
	private static Map<Integer, CommandModel> getSendableMap(List<CommandModel> commands)
	{
		Map<Integer, CommandModel> map = new HashMap<Integer, CommandModel>();
		for(CommandModel model : commands)
		{
			if(model.getType() != CommandType.SERVER_TYPE) continue;
			map.put(model.getOpcode(), model);
		}
		return map;
	}
	
	/**
	 * 将收到的命令与Method关联
	 * @param obj
	 * @param commands
	 * @param methodMap
	 * @return
	 */
	private static Map<Integer, MethodModel> getMethodModel(Object obj, List<CommandModel> commands, Map<Integer, Method> methodMap)
	{
		Map<Integer, MethodModel> map = new HashMap<Integer, MethodModel>();
		Method method = null;
		/**关联命令与Method**/
		for(CommandModel model : commands)
		{
			if(model.getType() == CommandType.SERVER_TYPE) continue;
			if(map.containsKey(model.getOpcode()))
			{
				StringBuilder builder = new StringBuilder("类[").append(obj.getClass().getName()).append("]命令[");
				builder.append(model.getOpcode()).append("]重复，放弃注册。");
				log.warn(builder.toString());
				continue;
			}
			method = methodMap.get(model.getOpcode());
			/**判断method是否为空**/
			if(method == null)
			{
				StringBuilder builder = new StringBuilder("类[").append(obj.getClass().getName()).append("]命令[");
				builder.append(model.getOpcode()).append("]没有对应的可执行的方法。");
				log.warn(builder.toString());
				continue;
			}
			/**创建MethodModel**/
			MethodModel methodModel = new MethodModel(obj, method, model.getCommandInfoModel() == null ? new CommandInfoModel[0] : model.getCommandInfoModel());
			map.put(model.getOpcode(), methodModel);
			/**移除已经对应命令的Method**/
			methodMap.remove(model.getOpcode());
		}
		for(Entry<Integer, Method> e : methodMap.entrySet())
		{
			StringBuilder builder = new StringBuilder("类[").append(obj.getClass().getName()).append("]方法[");
			builder.append(e.getValue().getName()).append("]没有找到对应的命令[").append(e.getKey()).append("]的解析方式.");
			log.warn(builder.toString());
		}
		return map;
	}
	
	/**
	 * 获取所有注解对应的控制器方法
	 * @param clz		控制器对象
	 * @param methods	所有方法实体列表
	 * @return
	 * @throws ModuleRegisteredException 
	 * @create	2013-9-11	darren.ouyang
	 */
	private static Map<Integer, Method> getMethodMap(Class<? extends IController> clz, 
			Method[] methods) throws ModuleRegisteredException 
	{
		Map<Integer, Method> methodMap = new HashMap<Integer, Method>();
		
		for (Method method : methods)
		{
			if(method.isAnnotationPresent(MethodAnnotation.class))
			{
				int id = getKey(clz, method);
				if (methodMap.containsKey(id))
				{
					Method older = methodMap.get(id);
					StringBuilder builder = new StringBuilder("类[").append(clz.getName()).
							append("]方法[").append(method.getName()).append("]的key以与方法[").
							append(older.getName()).append("]的key重复。");
					log.info(builder.toString());
					
					methodMap.put(id, method);
				}
				methodMap.put(id, method);
			}
			else
			{
				if(!"getCommands".equals(method.getName()))
				{
					StringBuilder builder = new StringBuilder("类[").append(clz.getName()).
							append("]方法[").append(method.getName()).
							append("]没有进行MethodAnnotation注解。");
					log.warn(builder.toString());
				}
			}
		}
		
		return methodMap;
	}
	
	/**
	 * 获取控制器中注解的Key
	 * @param clz		控制器的clss对象
	 * @param method	控制器方法实体
	 * @return
	 * @throws ModuleRegisteredException
	 * @create	2013-9-11	darren.ouyang
	 */
	private static int getKey (Class<? extends IController> clz, Method method) 
			throws ModuleRegisteredException
	{
		MethodAnnotation annotation = method.getAnnotation(MethodAnnotation.class);
		/* 错误抛出注册异常 */
		if (annotation.key() == MethodAnnotation.DEFAULT_VALUE)
		{
			StringBuilder builder = new StringBuilder("类[").append(clz.getName()).
					append("]方法[").append(method.getName()).append("]注解的值错误。");
			log.warn(builder.toString());
			throw new ModuleRegisteredException(builder.toString());
		}
		
		return annotation.key();
	}
}
